package com.sxhuangjiang.websocketdemo.websocket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 提供一个“订阅通道” ，客户端连接到此地址后，可以进行双向通信，
 * 当然，demo演示的是服务器向客户端推送警告信息
 * @author huang
 * @version V1.0
 * @date 2019-12-03 9:08
 */
@ServerEndpoint("/ws/topic")
@Component
@Slf4j
public class TopicServer {

    //concurrent包的线程安全Set，TopicServer。
    private static CopyOnWriteArraySet<TopicServer> topicServerSet = new CopyOnWriteArraySet<>();

    //与某个客户端的连接会话
    //session是websocket的session，通过他，可以向其对应的客户端发送消息，客户端的onMessage事件会触发。
    private Session session;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        topicServerSet.add(this);     //加入set中
        log.debug("onOpen -----> {}加入到订阅者列表，有消息将可以选择发过去。",session.getId());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        topicServerSet.remove(this);  //从set中删除
        log.debug("onClose -----> {}取消订阅，消息将不再发过去。",session.getId());
    }

    /**
     * 收到客户端消息后调用的方法，demo这里是将message稍加处理之后返回。
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        String returnMsg = "服务器收到客户端的信息："+message+"；服务器明白了。";
        try {
            session.getBasicRemote().sendText(returnMsg);
        } catch (IOException e) {
            log.error(e.toString());
        }
    }

    /**
     * websocket发生错误时触发
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 向所有此订阅者群发消息，所有客户端都可以收到信息。
     * 注意，本demo内的推送是不具有消息队列的特性的，无法保存队列中的消息，如果某一个客户端在此期间离线了，
     * 那此客户端将无法接受到离线期间的消息了。
     * @param message 群发的消息。
     * @return 成功推送的客户端个数。可用于统计推送到达率
     */
    public static int pushMessage(String message){
        AtomicInteger success_count = new AtomicInteger();
        topicServerSet.forEach(topicServer -> {
            try {
                topicServer.session.getBasicRemote().sendText(message);
                success_count.getAndIncrement();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        return success_count.get();
    }


}
